# -*- coding: utf-8 -*-
import numpy as np

class SlidingWindow:
    def __init__(self, maxlen):
        self.buf = np.zeros(maxlen, dtype=np.float32)
        self.ptr = 0
        self.full = False

    def reset(self):
        self.ptr = 0
        self.full = False

    def append(self, val):
        self.buf[self.ptr] = val
        self.ptr = (self.ptr + 1) % len(self.buf)
        if self.ptr == 0:
            self.full = True

    def sum(self):
        if self.full:
            return np.sum(self.buf)
        else:
            return np.sum(self.buf[:self.ptr])

    def mean(self):
        if self.full:
            return np.mean(self.buf)
        else:
            return np.mean(self.buf[:self.ptr])


class DecayThenFlatSchedule():

    def __init__(self,
                 start,
                 finish,
                 time_length,
                 decay="exp"):

        self.start = start
        self.finish = finish
        self.time_length = time_length
        if self.time_length == 0:
            self.delta = self.start - self.finish
        else:
            self.delta = (self.start - self.finish) / self.time_length
            self.decay = decay

            if self.decay in ["exp"]:
                self.exp_scaling = (-1) * self.time_length / np.log(self.finish) if self.finish > 0 else 1

    def eval(self, T):
        if self.time_length == 0:
            return self.finish
        if self.decay in ["linear"]:
            return max(self.finish, self.start - self.delta * T)
        elif self.decay in ["exp"]:
            return min(self.start, max(self.finish, np.exp(- T / self.exp_scaling)))
        elif self.decay in ["cosine"]:
            if T >= self.time_length:
                return self.finish
            progress = T / self.time_length
            return self.finish + (self.start - self.finish) * 0.5 * (1 + np.cos(np.pi * progress))
        elif self.decay in ["fixed"]:
            return self.start
        

